package aqu.persistencia.utilidad;

import java.math.BigInteger;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Property;
import org.hibernate.criterion.Restrictions;

public abstract class HibernateCrud {

    protected Object myClass = new Object(); // tipo de objeto
    protected String TABLE = ""; // nombre de la tabla

    /**
     * Obtener un elemento a partir de su identificador
     * Se supone que en todas las entidades existe el atributo id que las identifica
     * @param id
     * @return
     */
    public Object getElementById(String id) {
        Object obj = new Object();
        // obtener la sesion actual
        Session session = HibernateUtil.getSessionFactory().openSession();
        try {
            // comenzar la transaccion
            session.beginTransaction();
            // cargar objeto por clave
            obj = session.createCriteria(myClass.getClass()).add(Restrictions.eq("id", id)).uniqueResult();
            // confirmar transaccion
            session.getTransaction().commit();
        } catch (HibernateException e) {
            System.out.println("Error en getElementById: " + e);
            // deshacer transaccion
            this.rollback();
            session.getTransaction().rollback();
        } finally {
            session.close();
        }
        return obj;
    }

    /**
     * Guardar un objeto en la base de datos
     * @param o
     */
    public boolean create(Object o) {
        // obtener la sesion actual
        Session session = HibernateUtil.getSessionFactory().openSession();
        try {
            // comenzar la transaccion
            session.beginTransaction();
            // almacenarlo
            session.save(o);
            // confirmar transaccion
            session.getTransaction().commit();
        } catch (HibernateException e) {

            if (e.getCause() == null) {
                MessagesController.addInfo("Error al Crear: ", e.toString() + " Notificar al Administrador");
            } else {
                MessagesController.addInfo("Error al Crear: ", e.getCause().getMessage() + " Notificar al Administrador");
            }
            // deshacer transaccion

            this.rollback();
            session.getTransaction().rollback();
            return false;
        } finally {
            session.close();
        }
        return true;
    }

   

    /**
     * Actualizar un objeto en la base de datos
     * @param o
     */
    public boolean update(Object o) {
        // obtener la sesion actual
        Session session = HibernateUtil.getSessionFactory().openSession();
        try {
            // comenzar la transaccion
            session.beginTransaction();
            // actualizarlo
            session.saveOrUpdate(o);
            // confirmar transaccion
            session.getTransaction().commit();
        } catch (HibernateException e) {
            System.out.println("Error en update: " + e);
            // deshacer transaccion
            this.rollback();
            session.getTransaction().rollback();
            MessagesController.addInfo("Error al Actualizar: ", e.getCause().getMessage() + "Notificar al Administrador");
            return false;
        } finally {
            session.close();
        }
        return true;
    }

    /**
     * Borrar un objeto de la base de datos
     * @param o
     */
    public boolean delete(Object o) {
        if (o == null) {
            return false;
        }
        // obtener la sesion actual
        Session session = HibernateUtil.getSessionFactory().openSession();
        try {
            // comenzar la transaccion
            session.beginTransaction();
            // borrarlo
            session.delete(o);
            // confirmar transaccion
            session.getTransaction().commit();
        } catch (HibernateException e) {
            System.out.println("Error en delete: " + e);
            // deshacer transaccion
            this.rollback();
            session.getTransaction().rollback();
            MessagesController.addInfo("Error al Eliminar: ", e.getCause().getMessage() + "Notificar al Administrador");

            return false;
        } finally {
            session.close();
        }
        return true;
    }

    /**
     * Obtener la lista de registros para una entidad
     * @return
     */
    public List getList() {
        // obtener la sesion actual
        Session session = HibernateUtil.getSessionFactory().openSession();
        List result = new ArrayList();
        try {
            // comenzar la transaccion
            session.beginTransaction();
            // obtener la lista de eventos
            result = session.createCriteria(myClass.getClass()).list();
            // confirmar transaccion
            session.getTransaction().commit();
        } catch (HibernateException e) {
            System.out.println("Error en getList: " + e);
            // deshacer transaccion
            this.rollback();
            session.getTransaction().rollback();
        } finally {
            session.close();
        }

        return result;
    }

    public List getListOrdenado(String campoOrden) {
        // obtener la sesion actual
        Session session = HibernateUtil.getSessionFactory().openSession();
        List result = new ArrayList();
        try {
            // comenzar la transaccion
            session.beginTransaction();
            // obtener la lista de eventos
            result = session.createCriteria(myClass.getClass()).addOrder(Property.forName(campoOrden).asc()).list();
            // confirmar transaccion
            session.getTransaction().commit();
        } catch (HibernateException e) {
            System.out.println("Error en getList: " + e);
            // deshacer transaccion
            this.rollback();
            session.getTransaction().rollback();
        } finally {
            session.close();
        }

        return result;
    }

    /**
     * Obtener una lista de registros de una entidad limitada
     * @param ini Indice inicial
     * @param batch Cantidad de registros a obtener
     * @return
     */
    public List getSubList(int ini, int batch) {
        // obtener la sesion actual
        Session session = HibernateUtil.getSessionFactory().openSession();
        List result = new ArrayList();
        try {
            // comenzar la transaccion
            session.beginTransaction();
            // obtener la lista de eventos
            List aux = session.createCriteria(myClass.getClass()).list();
            for (int i = ini; i < aux.size() && i < ini + batch; i++) {
                result.add(aux.get(i));
            }
            // confirmar transaccion
            session.getTransaction().commit();
        } catch (HibernateException e) {
            System.out.println("Error en getSubList: " + e);
            // deshacer transaccion
            this.rollback();
            session.getTransaction().rollback();
        } finally {
            session.close();
        }
        return result;
    }

    /**
     * Cantidad de registros de una entidad
     * @return
     */
    public int getListCount() {
        Number count = 0;
        // obtener la sesion actual
        Session session = HibernateUtil.getSessionFactory().openSession();
        try {
            // comenzar la transaccion
            session.beginTransaction();
            // obtener la cantidad
            count = (Number) session.createCriteria(myClass.getClass()).setProjection(Projections.rowCount()).uniqueResult();
            // confirmar transaccion
            session.getTransaction().commit();
        } catch (HibernateException e) {
            System.out.println("Error en getListCount: " + e);
            // deshacer transaccion
            this.rollback();
            session.getTransaction().rollback();
        } finally {
            session.close();
        }
        return count.intValue();
    }

    /**
     * Comprobar si esta repetido un objeto a partir de un valor de un atributo
     * @param attr
     * @param value
     * @return
     */
    public boolean checkRepeated(String attr, String value) {
        Object obj = new Object();
        // obtener la sesion actual
        Session session = HibernateUtil.getSessionFactory().openSession();
        try {
            // comenzar la transaccion
            session.beginTransaction();
            // cargar objeto por clave
            obj = session.createCriteria(myClass.getClass()).add(Restrictions.eq(attr, value)).uniqueResult();
            // confirmar transaccion
            session.getTransaction().commit();
        } catch (HibernateException e) {
            System.out.println("Error en checkRepeated: " + e);
            // deshacer transaccion
            this.rollback();
            session.getTransaction().rollback();
        } finally {
            session.close();
        }
        return obj != null ? true : false;
    }

    public List buscarConNombreQuery(String nameQuery, Map parametros) {
        // obtener la sesion actual
        Session session = HibernateUtil.getSessionFactory().openSession();
        List result = new ArrayList();
        try {
            // comenzar la transaccion
            session.beginTransaction();
            // obtener la lista de eventos
            result = session.getNamedQuery(nameQuery).setProperties(parametros).list();

            // confirmar transaccion
            session.getTransaction().commit();
        } catch (HibernateException e) {

            System.out.println("Error en getSubList: " + e);
            // deshacer transaccion
            this.rollback();
            session.getTransaction().rollback();
        } finally {
            session.close();
        }
        return result;
    }

    /**
     * Obtener la sesion de hibernate para el acceso a la base de datos
     * @return
     */
//    protected Session getSession(){
//        Session session = null;
//        try {
//            session = HibernateUtil.getSessionFactory().openSession();
//            if(!session.isOpen()){
//                session = HibernateUtil.getSessionFactory().openSession();
//            }
//        } catch(Exception e){
//            session = HibernateUtil.getSessionFactory().openSession();
//        }
//        return session;
//    }
    /**
     * Obtener el nombre de la tabla al que hace referencia
     * @return
     */
    public String getTableName() {
        return TABLE;
    }

    public Iterator buscarConSQLQuery(String nameQuery) {
        // obtener la sesion actual
        Session session = HibernateUtil.getSessionFactory().openSession();
        Iterator result = null;
        try {
            // comenzar la transaccion
            session.beginTransaction();
            // obtener la lista de eventos
            result = session.createSQLQuery(nameQuery).list().iterator();

            // confirmar transaccion
            session.getTransaction().commit();
        } catch (HibernateException e) {
            System.out.println("Error en getSubList: " + e);
            // deshacer transaccion
            this.rollback();
            session.getTransaction().rollback();
        } finally {
            session.close();
        }
        return result;
    }

    public int getTamanoColumna(String nombretabla, String columna) {
        int retorno = 0;
//        String sql = "select CHARACTER_MAXIMUM_LENGTH as TMaximo "
//                + " from "
//                + "information_schema.columns c where TABLE_NAME='" + nombretabla + "' and"
//                + " COLUMN_NAME='" + columna + "'";
        // obtener la sesion actual
        Session session = HibernateUtil.getSessionFactory().openSession();
        Iterator result = null;
        try {
            // comenzar la transaccion
            session.beginTransaction();
            // obtener la lista de eventos



            DatabaseMetaData metadata = session.connection().getMetaData();
            ResultSet resultSet = metadata.getColumns(null, null, nombretabla, columna);
            while (resultSet.next()) {
                String name = resultSet.getString("COLUMN_NAME");

                String type = resultSet.getString("TYPE_NAME");
                int size = resultSet.getInt("COLUMN_SIZE");
                retorno = size;

            }
//            result = session.createSQLQuery(sql).list().iterator();
//            if (result.hasNext()) {
//                int row = ((BigInteger) result.next()).intValue();
//                retorno = row;
//            }
            // confirmar transaccion
            session.getTransaction().commit();
        } catch (SQLException e) {
            System.out.println("Error en getSubList: " + e);
            // deshacer transaccion
            this.rollback();
            session.getTransaction().rollback();
        } finally {
            session.close();
        }
        return retorno;
    }

    public int getMaximo(String nombretabla, String columna) {
        int retorno = 0;
        String sql = "SELECT MAX(CAST(" + columna + " as SIGNED)) FROM " + nombretabla;
        // obtener la sesion actual
        Session session = HibernateUtil.getSessionFactory().openSession();
        Iterator result = null;
        try {
            // comenzar la transaccion
            session.beginTransaction();
            // obtener la lista de eventos
            result = session.createSQLQuery(sql).list().iterator();
            try {
                if (result.hasNext()) {
                    int row = ((BigInteger) result.next()).intValue();
                    retorno = row;
                }
            } catch (Exception e) {
                retorno = 0;
            }
            // confirmar transaccion
            session.getTransaction().commit();
        } catch (HibernateException e) {
            System.out.println("Error en getSubList: " + e);
            // deshacer transaccion
            this.rollback();
            session.getTransaction().rollback();
        } finally {
            session.close();
        }
        return retorno;
    }

    /**
     * Hacer rollback y que no se termine la aplicacion tras un fallo
     */
    public void rollback() {
        Session session = HibernateUtil.getSessionFactory().openSession();
        try {
            // deshacer transaccion
            session.getTransaction().rollback();
        } catch (HibernateException e) {
            System.out.println("Error en rollback: " + e);
        } finally {
            session.close();
        }
    }
}
